package start

import (
	"context"
	"fmt"
	"gitee.com/filters/logger"
	"gitee.com/filters/reader/wviper"
	"gitee.com/filters/start/proxy"
	"gitee.com/filters/start/refer"
	"gitee.com/filters/start/safe_map"
	"gitee.com/filters/utils/conversion"
	"github.com/gin-gonic/gin"
	"github.com/robfig/cron/v3"
	"github.com/rs/zerolog/log"
	"github.com/spf13/cast"
	"google.golang.org/grpc"
	"google.golang.org/grpc/encoding"
	"google.golang.org/grpc/reflection"
	"io"
	"net"
	"net/http"
	"os"
	"os/signal"
	"reflect"
	"strings"
	"syscall"
	"time"
)

type StarterInterface interface {
	LoadConfig(opt ...wviper.Option) error
	Run() error
}

var starter *Starter

const (
	GrpcPort = "grpc_port"
)

type GrpcConfig struct {
	Service interface{}
	Srv     interface{}
	SrvDesc grpc.ServiceDesc
}

type Starter struct {
	logx       safe_map.ConfigSafeMap
	readConfig []wviper.Option
	es         *gin.Engine
	gres       *GrpcConfig
	err        error
}

var (
	ginConfig safe_map.ConfigSafeMap
)

// CronTabObj 定时服务结构体
type CronTabObj struct {
	Name    string
	CronTab string
	Handler func() error
}

// GetGinConfig ...
func GetGinConfig() any {
	return config
}

// GetSrv 获取gin的服务
func (s *Starter) GetSrv() *gin.Engine {
	return s.es
}

type Option func(*Starter)

func WithReadConfig(options ...wviper.Option) Option {
	return func(s *Starter) {
		s.readConfig = options
	}
}

func WithGrpcConfig(cfg *GrpcConfig) Option {
	return func(s *Starter) {
		s.gres = cfg
	}
}

// New 初始化结构体
func New(options ...Option) *Starter {
	var (
		err error
	)
	start := &Starter{
		es: gin.Default(),
	}
	for _, option := range options {
		option(start)
	}
	config, err = wviper.NewSetting(start.readConfig...)
	if err != nil {
		panic(err)
	}
	if _, ok := config.Get(GinConfig).(map[string]interface{}); !ok {
		start.err = refer.ErrorNotFoundConfigInfo
	}
	ginConfig.Data = config.Get(GinConfig).(map[string]interface{})
	starter = start
	return start
}

// HandleRouter 绑定路由
func (s *Starter) HandleRouter(route func(r *gin.Engine)) *Starter {
	route(s.es)
	return s
}

// HandleGrpc 启动grpc服务
func (s *Starter) HandleGrpc() *Starter {
	if s.gres == nil {
		s.err = refer.ErrorNotFoundConfigInfo
		return s
	}

	t := reflect.TypeOf(s.gres.Service)
	vs := reflect.ValueOf(s.gres.SrvDesc)
	grpcCli := proxy.NewClient()
	for i := 0; i < t.NumMethod(); i++ {
		path := "/" + strings.Join([]string{vs.Field(0).String(), t.Method(i).Name}, "/")
		s.es.POST(path, func(ctx *gin.Context) {
			data, err := io.ReadAll(ctx.Request.Body)
			if err != nil {
				ctx.String(http.StatusBadGateway, err.Error())
				return
			}
			r, err := grpcCli.Post(fmt.Sprintf("127.0.0.1:%d", ginConfig.Get(GrpcPort)), path, data)
			if err != nil {
				ctx.String(http.StatusBadGateway, err.Error())
				return
			}
			ctx.String(http.StatusBadGateway, string(r))
		})
	}
	return s
}

// HandleAddFunc 定时执行函数
func (s *Starter) HandleAddFunc(list []*CronTabObj) *Starter {
	c := cron.New(cron.WithSeconds())
	for index, crontabObj := range list {
		func(id int, obj *CronTabObj) {
			_, _ = c.AddJob(
				obj.CronTab, //一分钟
				cron.FuncJob(func() {
					_ = obj.Handler()
				}))
		}(index, crontabObj)
	}
	c.Start() //启动crond
	return s
}

// BindField 绑定数据
func (s *Starter) BindField(f ...interface{}) *Starter {
	fmt.Println(config.AllSettings())
	for _, i2 := range f {
		conversion.ToStruct(config.AllSettings(), i2)
	}

	return s
}

// UseLog 日志记录
func (s *Starter) UseLog() *Starter {
	s.err = logger.NewLog(logger.WithWritePath(s.logx.Get(logger.LogPathName).(string)),
		logger.WithLogExt(s.logx.Get(logger.LogExtName).(string))).CurLog().WrapFormat()
	return s
}

// HandleMiddleware 绑定中间件
func (s *Starter) HandleMiddleware(middles ...gin.HandlerFunc) *Starter {
	s.es.Use(middles...)
	return s
}

// SampleRun 简单启动，非平滑启动
func (s *Starter) SampleRun() error {
	if s.err != nil {
		return s.err
	}

	if s.gres != nil {
		go s.GrpcRun()
	}
	gin.SetMode(ginConfig.Get("run_mode").(string))
	s.err = s.es.Run(":" + cast.ToString(ginConfig.Get("http_port").(int)))
	if s.err != nil {
		fmt.Println("启动失败...")
	}
	return s.err
}

// GrpcRun grpc启动
func (s *Starter) GrpcRun() {
	lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", ginConfig.Get(GrpcPort)))
	if err != nil {
		s.err = err
		return
	}
	// 实例化grpc服务端
	encoding.RegisterCodec(proxy.JSON{})
	conn := grpc.NewServer()

	// 反射注册Grpc服务
	ss := reflect.ValueOf(s.gres.Srv)
	args := []reflect.Value{reflect.ValueOf(conn), reflect.ValueOf(s.gres.Service)}
	ss.Call(args)
	// 往grpc服务端注册反射服务
	reflection.Register(conn)

	// 启动grpc服务
	if err := conn.Serve(lis); err != nil {
		s.err = err
		return
	}
	return
}

// Run 启动
func (s *Starter) Run() error {
	if s.err != nil {
		return s.err
	}
	gin.SetMode(ginConfig.Get("run_mode").(string))
	srv := &http.Server{
		Addr:    ":" + cast.ToString(ginConfig.Get("http_port").(int)),
		Handler: s.es,
	}
	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Err(err).Msg("Run err")
		}
	}()
	// wait for interrupt signal to gracefully shutdown the server with
	// a timeout of 10 seconds.
	quit := make(chan os.Signal, 1)
	// kill (no param) default send syscall.SIGTERM
	// kill -2 is syscall.SIGINT
	// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Info().Msg("Shutdown Server ...")
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		log.Err(err).Msg("Server Shutdown ...")
	}
	log.Info().Msg("Server exiting")
	return nil
}
